home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 68K / Demo / pdist / cvslock.py < prev    next >
Text File  |  1996-05-20  |  6KB  |  281 lines

  1. """CVS locking algorithm.
  2.  
  3. CVS locking strategy
  4. ====================
  5.  
  6. As reverse engineered from the CVS 1.3 sources (file lock.c):
  7.  
  8. - Locking is done on a per repository basis (but a process can hold
  9. write locks for multiple directories); all lock files are placed in
  10. the repository and have names beginning with "#cvs.".
  11.  
  12. - Before even attempting to lock, a file "#cvs.tfl.<pid>" is created
  13. (and removed again), to test that we can write the repository.  [The
  14. algorithm can still be fooled (1) if the repository's mode is changed
  15. while attempting to lock; (2) if this file exists and is writable but
  16. the directory is not.]
  17.  
  18. - While creating the actual read/write lock files (which may exist for
  19. a long time), a "meta-lock" is held.  The meta-lock is a directory
  20. named "#cvs.lock" in the repository.  The meta-lock is also held while
  21. a write lock is held.
  22.  
  23. - To set a read lock:
  24.  
  25.     - acquire the meta-lock
  26.     - create the file "#cvs.rfl.<pid>"
  27.     - release the meta-lock
  28.  
  29. - To set a write lock:
  30.  
  31.     - acquire the meta-lock
  32.     - check that there are no files called "#cvs.rfl.*"
  33.         - if there are, release the meta-lock, sleep, try again
  34.     - create the file "#cvs.wfl.<pid>"
  35.  
  36. - To release a write lock:
  37.  
  38.     - remove the file "#cvs.wfl.<pid>"
  39.     - rmdir the meta-lock
  40.  
  41. - To release a read lock:
  42.  
  43.     - remove the file "#cvs.rfl.<pid>"
  44.  
  45.  
  46. Additional notes
  47. ----------------
  48.  
  49. - A process should read-lock at most one repository at a time.
  50.  
  51. - A process may write-lock as many repositories as it wishes (to avoid
  52. deadlocks, I presume it should always lock them top-down in the
  53. directory hierarchy).
  54.  
  55. - A process should make sure it removes all its lock files and
  56. directories when it crashes.
  57.  
  58. - Limitation: one user id should not be committing files into the same
  59. repository at the same time.
  60.  
  61.  
  62. Turn this into Python code
  63. --------------------------
  64.  
  65. rl = ReadLock(repository, waittime)
  66.  
  67. wl = WriteLock(repository, waittime)
  68.  
  69. list = MultipleWriteLock([repository1, repository2, ...], waittime)
  70.  
  71. """
  72.  
  73.  
  74. import os
  75. import time
  76. import stat
  77. import pwd
  78.  
  79.  
  80. # Default wait time
  81. DELAY = 10
  82.  
  83.  
  84. # XXX This should be the same on all Unix versions
  85. EEXIST = 17
  86.  
  87.  
  88. # Files used for locking (must match cvs.h in the CVS sources)
  89. CVSLCK = "#cvs.lck"
  90. CVSRFL = "#cvs.rfl."
  91. CVSWFL = "#cvs.wfl."
  92.  
  93.  
  94. class Error:
  95.  
  96.     def __init__(self, msg):
  97.         self.msg = msg
  98.  
  99.     def __repr__(self):
  100.         return repr(self.msg)
  101.  
  102.     def __str__(self):
  103.         return str(self.msg)
  104.  
  105.  
  106. class Locked(Error):
  107.     pass
  108.  
  109.  
  110. class Lock:
  111.  
  112.     def __init__(self, repository = ".", delay = DELAY):
  113.         self.repository = repository
  114.         self.delay = delay
  115.         self.lockdir = None
  116.         self.lockfile = None
  117.         pid = `os.getpid()`
  118.         self.cvslck = self.join(CVSLCK)
  119.         self.cvsrfl = self.join(CVSRFL + pid)
  120.         self.cvswfl = self.join(CVSWFL + pid)
  121.  
  122.     def __del__(self):
  123.         print "__del__"
  124.         self.unlock()
  125.  
  126.     def setlockdir(self):
  127.         while 1:
  128.             try:
  129.                 self.lockdir = self.cvslck
  130.                 os.mkdir(self.cvslck, 0777)
  131.                 return
  132.             except os.error, msg:
  133.                 self.lockdir = None
  134.                 if msg[0] == EEXIST:
  135.                     try:
  136.                         st = os.stat(self.cvslck)
  137.                     except os.error:
  138.                         continue
  139.                     self.sleep(st)
  140.                     continue
  141.                 raise Error("failed to lock %s: %s" % (
  142.                     self.repository, msg))
  143.  
  144.     def unlock(self):
  145.         self.unlockfile()
  146.         self.unlockdir()
  147.  
  148.     def unlockfile(self):
  149.         if self.lockfile:
  150.             print "unlink", self.lockfile
  151.             try:
  152.                 os.unlink(self.lockfile)
  153.             except os.error:
  154.                 pass
  155.             self.lockfile = None
  156.  
  157.     def unlockdir(self):
  158.         if self.lockdir:
  159.             print "rmdir", self.lockdir
  160.             try:
  161.                 os.rmdir(self.lockdir)
  162.             except os.error:
  163.                 pass
  164.             self.lockdir = None
  165.  
  166.     def sleep(self, st):
  167.         sleep(st, self.repository, self.delay)
  168.  
  169.     def join(self, name):
  170.         return os.path.join(self.repository, name)
  171.  
  172.  
  173. def sleep(st, repository, delay):
  174.     if delay <= 0:
  175.         raise Locked(st)
  176.     uid = st[stat.ST_UID]
  177.     try:
  178.         pwent = pwd.getpwuid(uid)
  179.         user = pwent[0]
  180.     except KeyError:
  181.         user = "uid %d" % uid
  182.     print "[%s]" % time.ctime(time.time())[11:19],
  183.     print "Waiting for %s's lock in" % user, repository
  184.     time.sleep(delay)
  185.  
  186.  
  187. class ReadLock(Lock):
  188.  
  189.     def __init__(self, repository, delay = DELAY):
  190.         Lock.__init__(self, repository, delay)
  191.         ok = 0
  192.         try:
  193.             self.setlockdir()
  194.             self.lockfile = self.cvsrfl
  195.             fp = open(self.lockfile, 'w')
  196.             fp.close()
  197.             ok = 1
  198.         finally:
  199.             if not ok:
  200.                 self.unlockfile()
  201.             self.unlockdir()
  202.  
  203.  
  204. class WriteLock(Lock):
  205.  
  206.     def __init__(self, repository, delay = DELAY):
  207.         Lock.__init__(self, repository, delay)
  208.         self.setlockdir()
  209.         while 1:
  210.             uid = self.readers_exist()
  211.             if not uid:
  212.                 break
  213.             self.unlockdir()
  214.             self.sleep(uid)
  215.         self.lockfile = self.cvswfl
  216.         fp = open(self.lockfile, 'w')
  217.         fp.close()
  218.  
  219.     def readers_exist(self):
  220.         n = len(CVSRFL)
  221.         for name in os.listdir(self.repository):
  222.             if name[:n] == CVSRFL:
  223.                 try:
  224.                     st = os.stat(self.join(name))
  225.                 except os.error:
  226.                     continue
  227.                 return st
  228.         return None
  229.  
  230.  
  231. def MultipleWriteLock(repositories, delay = DELAY):
  232.     while 1:
  233.         locks = []
  234.         for r in repositories:
  235.             try:
  236.                 locks.append(WriteLock(r, 0))
  237.             except Locked, instance:
  238.                 del locks
  239.                 break
  240.         else:
  241.             break
  242.         sleep(instance.msg, r, delay)
  243.     return list
  244.  
  245.  
  246. def test():
  247.     import sys
  248.     if sys.argv[1:]:
  249.         repository = sys.argv[1]
  250.     else:
  251.         repository = "."
  252.     rl = None
  253.     wl = None
  254.     try:
  255.         print "attempting write lock ..."
  256.         wl = WriteLock(repository)
  257.         print "got it."
  258.         wl.unlock()
  259.         print "attempting read lock ..."
  260.         rl = ReadLock(repository)
  261.         print "got it."
  262.         rl.unlock()
  263.     finally:
  264.         print [1]
  265.         sys.exc_traceback = None
  266.         print [2]
  267.         if rl:
  268.             rl.unlock()
  269.         print [3]
  270.         if wl:
  271.             wl.unlock()
  272.         print [4]
  273.         rl = None
  274.         print [5]
  275.         wl = None
  276.         print [6]
  277.  
  278.  
  279. if __name__ == '__main__':
  280.     test()
  281.